<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>

  new 实例对象
  原型对象
  构造函数

  prototype __proto__ constructor 之间的三角恋

  对象继承：
  1. 原型链继承 存在引用值共享问题
  2. 构造函数继承（解决引用值共享问题），没办法拿到原型上的方法
  3. 组合继承（伪经典继承）
  4. 寄生组合继承(经典继承)
  5. 圣杯模式
  6. ES6 extends 继承
  7. 拷贝继承（不常用）

  <script>
    // 构造函数 首字母大写
    function Foo() {
      this.a = 1;
    }

    // 实例化对象 能将内部的this指向换成实例对象
    // let foo = new Foo()
    // console.log(foo);

    // 原型对象是所有实例对象的公共祖先
    // 实例有能力拿到原型上属性和方法
    // Foo.prototype = {
    //     aa: 10
    // }
    // Foo.prototype.aa = 10
    let foo = new Foo()
    // console.log(foo.aa);
    // console.log(foo);

    // 原型的两种访问方式
    // 系统推荐Foo.prototype修改原型
    // 如果需要从实例上访问原型 推荐用es6方法getPrototypeOf
    // console.log(foo.__proto__ === Foo.prototype); // true

    // console.log(Object.getPrototypeOf(foo));


    // console.log(Object.prototype === foo.__proto__.__proto__);

    // 原型链终点 Object.prototype(万物皆对向)
    // console.log(Object.prototype);

    // 原型上constructor指向构造函数
    //如果原型修改后constructor消失，那么再往下寻找“爷爷的”constructor

    // console.log(Foo.prototype.constructor === Foo);

    // *********************************************************

    // 继承
    /*
        // 1. 原型链继承 能拿到原型链上任意对象的属性和方法，包括“太爷爷的” 
        // [] -> Array.prototype -> Object.prototype
        // console.log([]);
        // console.log([].isPrototypeOf);

        function Super() {
          this.a = '111'
          this.arr = [1,2,3]
        }

        Super.prototype.say = function () {
          console.log('222');
        }

        function Sub() {}
        // 原型链继承
        Sub.prototype = new Super()

        // let sub1 = new Sub()
        // console.log(sub1.a);
        // sub1.say()
        // sub1 => Sub.prototype(new Super()) => Super.prototype

        // 原型链继承问题：引用值共享（基本类型不受影响）
        let sub1 = new Sub()
        sub1.a = '333'
        console.log(sub1.a);

        let sub2 = new Sub()
        console.log(sub2.a);

        sub1.arr.push(6)
        console.log(sub2.arr);

        */

    // 2. 构造函数继承（解决引用值共享问题）
    // 问题：没办法拿到原型上的方法
    /* 
     function Super() {
       this.arr = [1, 2, 3]
     }

     function Sub() {
       Super.call(this)
     }
     let sub1 = new Sub()
     let sub2 = new Sub()
     sub1.arr.push(6)
     console.log(sub2.arr);
      */

    // 3.组合继承（伪经典继承）
    /* 
      内部继承执行了两次
      第一步 Sub.prototype = new Super();原型链继承
      第二部是 Super.call(this) 构造函数继承
      构造函数继承会覆盖原型链继承
    */

    /* function Super() {
      this.arr = [1, 2, 3]
    }
    Super.prototype.say = function() {
      console.log(222);
    }

    function Sub() {
      Super.call(this)
    }

    Sub.prototype = new Super();

    let sub1 = new Sub()
    let sub2 = new Sub()
    sub1.arr.push(6)
    console.log(sub2.arr);
    sub1.say() */

    // 4. 寄生组合继承(经典继承)
    /* 
        function Super() {
          this.arr = [1, 2, 3]
        }
        Super.prototype.say = function () {
          console.log(222);
        }

        function Sub() {
          Super.call(this)
        }

        // 兼容es3写法
        // if (!Object.create) {
        //   Object.create = function(proto) {
        //     function F(){}
        //     F.prototype = proto
        //     return new F()
        //   }
        // }
        Sub.prototype = Object.create(Super.prototype)

        let sub1 = new Sub()
        let sub2 = new Sub()
        sub1.arr.push(6)
        console.log(sub2.arr);
        sub1.say()

     */

    // 5.圣杯模式（企业最常用） 
    function Teacher() {
      this.name = 'xxx'
    }
    function Student() {}
    function Buffer() {}
    Buffer.prototype = Teacher.prototype
    Student.prototype = new Buffer()
    Student.prototype.constructor = Student


    // ************************************************************
    // 面试题
    // console.log(Foo()); 打开是第一套方案；注释是第二套方案
    // 1.AO  2.var变量  3.实参形参赋值  4.函数声明  5.执行

    function Foo() {
      getName = function() {
        console.log(1);
      } // 没有变量关键字 暗示getName方法挂载到全局
      return this
    }
    Foo.getName = function() {
      console.log(2);
    }
    Foo.prototype.getName = function() {
      console.log(3);
    }
    var getName = function() {
      console.log(4);
    }

    function getName() {
      console.log(5);
    }

    // console.log(Foo()); // window

    Foo.getName(); // 2 // 2

    getName() // 1  // 4

    Foo().getName(); // 1  // 1

    getName(); // 1   // 1

    new Foo.getName(); // 2   // 2
    new Foo().getName(); // =》 (new Foo()).getName() 先实例化 3  // 3
    new new Foo().getName(); // 3   // 3



  </script>
</body>

</html>